/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.contexts;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.ScriptRuntimeException;
import cn.easyplatform.castor.Castors;
import cn.easyplatform.dos.FieldDo;
import cn.easyplatform.dos.Record;
import cn.easyplatform.entities.beans.list.ListBean;
import cn.easyplatform.entities.beans.table.TableBean;
import cn.easyplatform.entities.beans.task.TaskBean;
import cn.easyplatform.interceptor.CommandContext;
import cn.easyplatform.lang.Nums;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.vos.ListInitVo;
import cn.easyplatform.messages.vos.datalist.ListListInitVo;
import cn.easyplatform.type.Constants;
import cn.easyplatform.type.EntityType;
import cn.easyplatform.util.RuntimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.*;

/**
 * 二级工作信息管理
 *
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
class TaskContext implements Serializable, Cloneable {

    /**
     *
     */
    private static final long serialVersionUID = 362217585279849873L;

    private final static Logger log = LoggerFactory.getLogger(TaskContext.class);

    private Map<String, Object> systemVariables;// 系统参统

    private List<ListContext> dataList;// 当前页所属的列表

    private List<ReportContext> reportList;// 报表

    private List<SwiftContext> swiftList;// SWIFT电文

    private RecordContext rc;// 当前主记录

    protected Map<String, FieldDo> userVariables;// 公共变量

    private Map<String, String> children;// 所有该功能下的子功能集合

    /**
     * 正常功能
     *
     * @param tb
     * @param root
     */
    TaskContext(TaskBean tb, Map<String, Object> root) {
        systemVariables = new HashMap<String, Object>();
        dataList = new ArrayList<ListContext>();
        reportList = new ArrayList<ReportContext>();
        userVariables = new HashMap<String, FieldDo>();
        systemVariables.putAll(root);
        RuntimeUtils.initTask(systemVariables, tb);
        RuntimeUtils.initTaskVars(userVariables, tb);
        rc = new RecordContext(systemVariables, userVariables);
        children = new HashMap<String, String>();
    }

    /**
     * 列表open
     *
     * @param rc
     */
    TaskContext(RecordContext rc) {
        dataList = new ArrayList<ListContext>();
        reportList = new ArrayList<ReportContext>();
        systemVariables = rc.systemVariables;
        userVariables = rc.userVariables;
        children = new HashMap<String, String>();
        this.rc = rc;
    }

    /**
     * @param var
     */
    void setVariable(FieldDo var) {
        userVariables.put(var.getName(), var);
    }

    /**
     * @param record
     * @return
     */
    RecordContext createRecord(Record record) {
        return new RecordContext(record, systemVariables, userVariables);
    }

    /**
     * @return
     */
    RecordContext getRecordContext() {
        return rc;
    }

    /**
     * @param rc
     */
    void setRecordContext(RecordContext rc) {
        this.rc = rc;
    }

    /**
     * @param id
     */
    ReportContext setReport(String id, String entityId, String ids, char state, String table) {
        ReportContext cx = new ReportContext(this.rc, id, entityId, ids, state, table);
        reportList.add(cx);
        return cx;
    }

    /**
     * @param id
     * @return
     */
    ReportContext getReport(String id) {
        for (ReportContext rc : reportList) {
            if (rc.getId().equals(id))
                return rc;
        }
        return null;
    }

    /**
     * @return
     */
    Collection<ReportContext> getReports() {
        return reportList;
    }

    /**
     * @param iv
     * @param bean
     */
    ListContext setList(CommandContext cc, WorkflowContext ctx, ListInitVo iv,
                        ListBean bean) {
        if (iv instanceof ListListInitVo && !Constants.ACTIONBOX.equals(iv.getType())) {
            for (ListContext lc : dataList) {
                if (lc.getId().equals(iv.getId())) {
                    if (lc.getParams() != null)
                        lc.getParams().clear();
                    return lc;
                }
            }
        }
        ListContext lc = new ListContext(cc, ctx, iv, bean, systemVariables,
                userVariables);
        if (!Strings.isBlank(iv.getHost())) {
            boolean found = false;
            for (ListContext c : dataList) {
                if (c.getId().equals(iv.getHost())) {
                    c.children.add(iv.getId());
                    found = true;
                }
            }
            if (!found)
                throw new EasyPlatformWithLabelKeyException(
                        "context.datalist.from.not.found", iv.getId(),
                        iv.getHost());
        }
        dataList.add(lc);
        return lc;
    }

    void setList(ListContext lc) {
        dataList.add(lc);
    }

    /**
     * @param id
     * @return
     */
    SwiftContext setSwift(String id, TableBean table, boolean isEditable) {
        if (swiftList == null)
            swiftList = new ArrayList<SwiftContext>();
        SwiftContext sc = new SwiftContext(id, table, isEditable,
                systemVariables, userVariables);
        swiftList.add(sc);
        return sc;
    }

    void removeSwift(String id) {
        if (swiftList != null) {
            Iterator<SwiftContext> itr = swiftList.iterator();
            while (itr.hasNext()) {
                SwiftContext sc = itr.next();
                if (sc.getId().equals(id)) {
                    itr.remove();
                    break;
                }
            }
        }
    }

    /**
     * @param id
     * @return
     */
    SwiftContext getSwift(String id) {
        if (swiftList == null)
            return null;
        for (SwiftContext sc : swiftList) {
            if (sc.getId().equals(id))
                return sc;
        }
        return null;
    }

    /**
     * @return
     */
    List<SwiftContext> getSwifts() {
        if (swiftList == null)
            return Collections.emptyList();
        return swiftList;
    }

    /**
     * @param id
     */
    void removeList(String id) {
        Iterator<ListContext> itr = dataList.iterator();
        while (itr.hasNext()) {
            ListContext lc = itr.next();
            if (lc.getId().equals(id)) {
                itr.remove();
                break;
            }
        }
    }

    /**
     * @param id
     * @return
     */
    ListContext getList(String id) {
        for (ListContext lc : dataList) {
            if (lc.getId().equals(id))
                return lc;
        }
        return null;
        // throw new ContextException("context.datalist.not.found", id);
    }

    /**
     * @return
     */
    List<ListContext> getLists() {
        if (dataList == null)
            return Collections.emptyList();
        return dataList;
    }

    /**
     * @param name
     * @param value
     */
    void setParameter(String name, Object value) {
        systemVariables.put(name, value);
    }

    /**
     * @param name
     * @return
     */
    Object getParameter(String name) {
        return systemVariables.get(name);
    }

    /**
     * @param name
     * @return
     */
    String getParameterAsString(String name) {
        return (String) systemVariables.get(name);
    }

    /**
     * @param name
     * @return
     */
    boolean getParameterAsBoolean(String name) {
        Object obj = systemVariables.get(name);
        if (obj == null)
            return false;
        if (obj instanceof Boolean)
            return ((Boolean) obj).booleanValue();
        return obj.toString().equalsIgnoreCase("true");
    }

    /**
     * @param name
     * @return
     */
    char getParameterAsChar(String name) {
        Object obj = systemVariables.get(name);
        if (obj == null)
            return ' ';
        if (obj instanceof Character)
            return ((Character) obj).charValue();
        String s = obj.toString();
        if (!Strings.isBlank(s))
            return s.charAt(0);
        return ' ';
    }

    /**
     * @param name
     * @return
     */
    int getParameterAsInt(String name) {
        Object obj = systemVariables.get(name);
        if (obj == null)
            return 0;
        if (obj instanceof Number)
            return ((Number) obj).intValue();
        return Nums.toInt(obj, 0);
    }

    /**
     * @param type
     * @param name
     * @return
     */
    <T> T getAs(Class<T> type, String name) {
        return getAs(type, name, null);
    }

    /**
     * @param type
     * @param name
     * @param dft
     * @return
     */
    <T> T getAs(Class<T> type, String name, T dft) {
        Object obj = systemVariables.get(name);
        if (null == obj)
            return dft;
        return Castors.me().castTo(obj, type);
    }

    void appendChild(CommandContext cc, String cid, WorkflowContext child) {
        int om = child.getParameterAsInt("805");
        if (om == Constants.OPEN_EMBBED) {
            String id = children.remove(cid);
            if (id != null) {
                WorkflowContext wc = cc.getWorkflowContext(id);
                cc.getSync().unlock(wc.getId(), 0);
                cc.remove(wc.getId());
                wc.removeAll(cc);
                wc = null;
            }
            children.put(cid, child.getId());
        }
    }

    boolean removeChild(WorkflowContext child) {
        Iterator<String> itr = children.values().iterator();
        while (itr.hasNext()) {
            String id = itr.next();
            if (id.equals(child.getId())) {
                itr.remove();
                return true;
            }
        }
        return false;
    }

    void clear(CommandContext cc, boolean all) {
        if (all) {
            for (String childId : children.values()) {
                WorkflowContext wc = (WorkflowContext) cc.remove(childId);
                if (wc != null) {
                    cc.getSync().unlock(wc.getId(), 0);
                    if (log.isDebugEnabled())
                        log.debug("removeWorkflowContext->{}", wc);
                    wc.removeAll(cc);
                    wc = null;
                }
            }
            children.clear();
            dataList.clear();
        } else {// next
            Iterator<ListContext> itr = dataList.iterator();
            while (itr.hasNext()) {
                ListContext lc = itr.next();
                if (!lc.getType().equals(Constants.CATALOG)
                        && !lc.getType().equals(Constants.DETAIL)) {
                    itr.remove();
                    lc = null;
                }
            }
        }
    }

    Set<Map.Entry<String, String>> getChildren() {
        return children.entrySet();
    }

    String getChild(String cid) {
        return children.get(cid);
    }

    void onRollback(CommandContext cc) {
        Object src = systemVariables.get("821");
        if (src != null) {
            systemVariables.put("808", Constants.ON_ROLLBACK);
            String result = RuntimeUtils.eval(cc, src, rc);
            if (!result.equals("0000"))
                throw new ScriptRuntimeException(result, cc.getMessage(result,
                        rc));
        }
    }

    void onCommitted(CommandContext cc) {
        Object src = systemVariables.get("822");
        if (src != null) {
            systemVariables.put("808", Constants.ON_COMMITTED);
            String result = RuntimeUtils.eval(cc, src, rc);
            if (!result.equals("0000"))
                throw new ScriptRuntimeException(result, cc.getMessage(result,
                        rc));
        }
    }

    void onCommittedRollback(CommandContext cc) {
        Object src = systemVariables.get("823");
        if (src != null) {
            systemVariables.put("808", Constants.ON_COMMITTED_ROLLBACK);
            String result = RuntimeUtils.eval(cc, src, rc);
            if (!result.equals("0000"))
                throw new ScriptRuntimeException(result, cc.getMessage(result,
                        rc));
        }
    }

    void onPreCommit(CommandContext cc) {
        Object src = systemVariables.get("824");
        if (src != null) {
            systemVariables.put("808", Constants.ON_PRE_COMMIT);
            String result = RuntimeUtils.eval(cc, src, rc);
            if (!result.equals("0000"))
                throw new ScriptRuntimeException(result, cc.getMessage(result,
                        rc));
        }
    }

    void onPreCommitRollback(CommandContext cc) {
        Object src = systemVariables.get("825");
        if (src != null) {
            systemVariables.put("808", Constants.ON_PRE_COMMIT_ROLLBACK);
            String result = RuntimeUtils.eval(cc, src, rc);
            if (!result.equals("0000"))
                throw new ScriptRuntimeException(result, cc.getMessage(result,
                        rc));
        }
    }

    void next(CommandContext cc) {
        // 数据列表
        for (ListContext lc : dataList)
            lc.next(cc);
        // SWIFT
        if (swiftList != null) {
            for (SwiftContext sc : swiftList)
                sc.next(cc);
        }
    }

    void onBeforeCommit(CommandContext cc) {
        // 主体记录预先保存，后面的逻辑再处理主记录的id，如果id是自增长
        rc.save(cc);
        // 执行提交前逻辑
        Object src = systemVariables.get("819");
        if (src != null) {
            systemVariables.put("808", Constants.ON_BEFORE_COMMIT);
            String result = RuntimeUtils.eval(cc, src, rc);
            if (!result.equals("0000"))
                throw new ScriptRuntimeException(result, cc.getMessage(result,
                        rc));
        }
    }

    void onAfterCommit(CommandContext cc) {
        // 执行提交后逻辑
        Object src = systemVariables.get("820");
        if (src != null) {
            systemVariables.put("808", Constants.ON_AFTER_COMMIT);
            String result = RuntimeUtils.eval(cc, src, rc);
            if (!result.equals("0000"))
                throw new ScriptRuntimeException(result, cc.getMessage(result,
                        rc));
        }
    }

    void commit(CommandContext cc) {
        // 数据列表
        for (ListContext lc : dataList)
            lc.save(cc);
        // SWIFT
        if (swiftList != null) {
            for (SwiftContext sc : swiftList)
                sc.save(cc);
        }
        // 保存报表内容
        for (ReportContext report : reportList)
            report.save(cc);

        // 内嵌功能
        for (String id : children.values()) {
            WorkflowContext wc = cc.getWorkflowContext(id);
            wc.commit(cc);
        }
    }

    void reset(CommandContext cc) {
        boolean persist = rc.systemVariables.get("830").equals(
                EntityType.PAGE.getName())
                && rc.systemVariables.get("833") != null
                && rc.getParameterAsBoolean("815")
                && rc.getParameterAsChar("814") != 'R';
        if (persist && rc.getParameterAsChar("814") == 'C')
            rc.setParameter("814", 'U');
        for (ListContext lc : dataList)
            lc.reset();
        if (swiftList != null) {
            for (SwiftContext sc : swiftList)
                sc.reset();
        }
        for (String id : children.values()) {
            WorkflowContext wc = cc.getWorkflowContext(id);
            wc.resetPage(cc);
        }
    }

    void applyAll(CommandContext cc, String name, Object value) {
        rc.setParameter(name, value);
        for (ListContext lc : dataList)
            lc.applyAll(name, value);
        if (swiftList != null) {
            for (SwiftContext sc : swiftList)
                sc.applyAll(name, value);
        }
        for (String id : children.values()) {
            WorkflowContext wc = cc.getWorkflowContext(id);
            wc.applyAll(cc, name, value);
        }
    }

    @Override
    protected TaskContext clone() {
        try {
            TaskContext clone = (TaskContext) super.clone();
            clone.systemVariables = new HashMap<String, Object>(this.systemVariables);
            clone.rc = this.rc != null ? this.rc.clone() : null;
            return clone;
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }
}
